
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "gpio_key.h"
#include "gpio_utils.h"
#include "parse_config.h"
#include "uinput.h"
#include <sys/select.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#define PIO_BASE_ADDRESS 0x01C20800


static bool run_cmd(const char * cmd, char * output, size_t output_len) {

    // printf(">>%s\n",cmd) ;

    FILE *fp;

    /* Open the command for reading. */
    fp = popen(cmd, "r");
    if (fp == NULL) {
        return false ;
    }

    if(output && output_len>0) {
        fgets(output, output_len, fp) ;
    }

    pclose(fp);

    return true;
}



bool gpiokey_add(list_t * list, uint16_t pin, int code, pull_t pull) {

    // printf("gpio[%d] -> key[%d]; pull: %d\n", pin, code, pull) ;

    if(gpio_export(pin)!=0) {
        printf("export gpio failed: %d\n", pin) ;
        return false ;
    }
    if(gpio_set_dir(pin, "in")!=0) {
        printf("set gpio dir(in) failed: %d\n", pin) ;
        return false ;
    }
    // gpio_set_edge(pin, "both") ;
    // if (gpio_set_edge(pin, "both") < 0) {
    //     printf("set gpio edge(both) failed: %d\n", pin) ;
    //     return false;
    // }

    int fd = gpio_fd_open(pin, O_RDONLY);
    if (fd < 0) {
        printf("open gpio sysfs failed: fd=%d\n", fd) ;
        return false;
    }
    // printf("open gpio sysfs fd=%d\n",fd) ;

    if(pull!=PULL_NONE) {
        
        int port = pin / 32 ;
        int pin_num = pin % 32 ;

        int pulNum = pin_num / 16;
        int pulShift = pin_num % 16;
        int pullOffset = port * 0x24 + 0x1C + pulNum * 0x04 ;

        // printf("pull reg offset: %x \n", pullOffset) ;
        // printf("pull reg addr:%x \n", PIO_BASE_ADDRESS + pullOffset) ;

        char cmd[128] ;
        char output[128] ;
        sprintf(cmd, "/sbin/devmem 0x%x", PIO_BASE_ADDRESS + pullOffset) ;

        if(!run_cmd(cmd, output, sizeof(output))) {
            printf("run system cmd failded: %s\n", cmd) ;
            return false ;
        }

        int regval = strtoul(output, NULL, 16) ;

        int val = pull==PULL_UP? 1: 2 ;
        
        // printf("%s = %d; bits:%d, shift: %d\n",output, regval, val, pulShift * 2) ;
        regval&= ~(3 << (pulShift * 2)) ;
        regval|= val << (pulShift * 2) ;
    
        sprintf(cmd, "/sbin/devmem 0x%x w 0x%x", PIO_BASE_ADDRESS + pullOffset, regval) ;
        if(!run_cmd(cmd, NULL, 0)) {
            printf("run system cmd failded: %s\n", cmd) ;
            return false ;
        }
    }

    gpiokey_t * gk = malloc(sizeof(gpiokey_t)) ;
    memset(gk, 0, sizeof(gpiokey_t)) ;

    gk->pin = pin ;
    gk->code = code ;
    gk->pull = pull ;
    gk->fd = fd ;

    gpio_get_value(pin, &gk->val) ;

    list_append(list, (list_item_t*)gk) ;

    return true ;
}

int gpiokey_poll_fdset(list_t * list, fd_set * fdset) {
    int maxfd = 0 ;
    FOREACH_TYPE_LIST(list, gpiokey_t, gk){
        FD_SET(gk->fd, fdset);
        if( gk->fd > maxfd ) {
            maxfd = gk->fd ;
        }
    }
    return maxfd ;
}

void gpiokey_trigger(list_t * list, fd_set * fdset) {
    unsigned int val = 0 ;
    int keyval = 1 ;
    char buffer[2] ;
    FOREACH_TYPE_LIST(list, gpiokey_t, gk){
        if(FD_ISSET(gk->fd, fdset)) {

            
                lseek(gk->fd, 0, SEEK_SET);
                if (read(gk->fd, &buffer, 2) != 2) {
		            printf("read: %s\n", strerror(errno));
                    continue ;
                }


            printf("pin %d triggered -> key %d\n", gk->pin, gk->code) ;

            if( gpio_get_value(gk->pin, &val)!=0 ) {
                printf("get gpio input value failed:%d\n", gk->pin) ;
                continue;
            }

            printf("gpio %d trigger with %d\n", gk->pin, val) ;

            if(gk->pull==PULL_UP) {
                keyval = val==0? 1: 0 ;
            }
            else if(gk->pull==PULL_DOWN) {
                keyval = val==0? 0: 1 ;
            }

            printf("send key %d\n", keyval) ;

            // sendKey(item->code, keyval) ;
        }
    }
}


void gpiokey_loop(list_t * list) {
    unsigned int val = 0 ;
    int keyval = 1 ;
    FOREACH_TYPE_LIST(list, gpiokey_t, gk){
        if(gpio_get_value(gk->pin, &val)!=0) {
            continue;
        }
        if( val != gk->val ) {
            
            if(gk->pull==PULL_UP) {
                keyval = val==0? 1: 0 ;
            }
            else if(gk->pull==PULL_DOWN) {
                keyval = val==0? 0: 1 ;
            }
            sendKey(gk->code, keyval) ;

            printf("gpio(%d) %d -> %d ;sendKey(%d,%d)\n", gk->pin, gk->val, val, gk->code, keyval) ;
            gk->val = val ;
        }

    }
}